package org.study.mybatisplus.dynamic.boot.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.collect.*;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.study.mybatisplus.dynamic.boot.config.LocalCacheConfig;
import org.study.mybatisplus.dynamic.boot.config.SystemConstant;
import org.study.mybatisplus.dynamic.boot.dto.BrokerMessageDTO;
import org.study.mybatisplus.dynamic.boot.mapper.*;
import org.study.mybatisplus.dynamic.boot.model.*;
import org.study.mybatisplus.dynamic.boot.service.CollectorService;
import org.study.mybatisplus.dynamic.boot.util.TDengineUtils;
import org.study.mybatisplus.dynamic.boot.util.TDengineUtils.DateTypeEnum;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 作用描述
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2025年10月20日
 */
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
@Service
public class CollectorServiceImpl implements CollectorService {
    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
    private final LocalCacheConfig localCacheConfig;
    private final IotDeviceMapper iotDeviceMapper;
    private final IotDevicePropertyMapper iotDevicePropertyMapper;
    private final IotDatasourceMapper iotDatasourceMapper;

    @PostConstruct
    @Override
    public void initLocalCache() {
        Map<String, IotDevice> deviceMap = Maps.newHashMap();
        Map<String, Map<String, IotDeviceProperty>> propertyMapMap = Maps.newHashMap();
        Map<String, List<IotDeviceProperty>> propertyListMap = Maps.newHashMap();
        Multimap<String, Map<String, List<IotDeviceProperty>>> propertyMultimap = ArrayListMultimap.create();

        List<IotDevice> iotDeviceList = iotDeviceMapper.selectList(
                Wrappers.<IotDevice>query().lambda()
                        .eq(IotDevice::getIsDeleted, SystemConstant.DB_NOT_DELETED)
        );
        List<IotDeviceProperty> iotDevicePropertyList = iotDevicePropertyMapper.selectList(
                Wrappers.<IotDeviceProperty>query().lambda()
                        .eq(IotDeviceProperty::getIsDeleted, SystemConstant.DB_NOT_DELETED)
        );
        Map<Long, List<IotDeviceProperty>> iotDevicePropertyMap = iotDevicePropertyList.stream()
                .collect(Collectors.groupingBy(IotDeviceProperty::getDeviceId));

        iotDeviceList.forEach(iotDevice -> {
            Long deviceId = iotDevice.getId();
            String gatewayNo = iotDevice.getGatewayNo();
            String deviceNo = iotDevice.getDeviceNo();
            String gatewayNoAndDeviceNo = gatewayNo + ":" + deviceNo;
            deviceMap.put(gatewayNoAndDeviceNo, iotDevice);

            List<IotDeviceProperty> currentIotDevicePropertyList = iotDevicePropertyList.stream()
                    .filter(deviceProperty -> deviceId.equals(deviceProperty.getDeviceId()))
                    .collect(Collectors.toList());
            List<IotDeviceProperty> currentIotDevicePropertyList1 = iotDevicePropertyMap.get(deviceId);

            Map<String, IotDeviceProperty> tempPropertyMap = currentIotDevicePropertyList.stream()
                    .collect(Collectors.toMap(IotDeviceProperty::getPropertyNo, Function.identity()));
            /*if (propertyMapMap.containsKey(gatewayNoAndDeviceNo)) {
                Map<String, IotDeviceProperty> existMap = propertyMapMap.get(gatewayNoAndDeviceNo);
                existMap.putAll(tempPropertyMap);
                propertyMapMap.put(gatewayNoAndDeviceNo, existMap);
            } else {
                propertyMapMap.put(gatewayNoAndDeviceNo, tempPropertyMap);
            }*/
            propertyMapMap.merge(gatewayNoAndDeviceNo, tempPropertyMap, (oldMap, newMap) -> {
                oldMap.putAll(newMap);
                return oldMap;
            });

            // 特别注意：不要直接使用 currentIotDevicePropertyList，而要用 new ArrayList<>(currentIotDevicePropertyList) 创建一个临时的、新的List，不然数据会重复
            /*if (propertyListMap.containsKey(gatewayNoAndDeviceNo)) {
                List<IotDeviceProperty> existList = propertyListMap.get(gatewayNoAndDeviceNo);
                existList.addAll(new ArrayList<>(currentIotDevicePropertyList));
                propertyListMap.put(gatewayNoAndDeviceNo, existList);
            } else {
                propertyListMap.put(gatewayNoAndDeviceNo, new ArrayList<>(currentIotDevicePropertyList));
            }*/
            propertyListMap.merge(gatewayNoAndDeviceNo, new ArrayList<>(currentIotDevicePropertyList), (oldList, newList) -> {
                oldList.addAll(newList);
                return oldList;
            });

            Map<String, List<IotDeviceProperty>> deviceNoPropertyListMap = Maps.newHashMap();
            deviceNoPropertyListMap.put(deviceNo, new ArrayList<>(currentIotDevicePropertyList));
            propertyMultimap.put(gatewayNo, deviceNoPropertyListMap);
        });
        localCacheConfig.setDeviceMap(deviceMap);
        log.info("1、设备数(gatewayNo:deviceNo)：{}", deviceMap.size());
        localCacheConfig.setPropertyMapMap(propertyMapMap);
        {
            AtomicInteger atomicInteger = new AtomicInteger(0);
            propertyMapMap.forEach((gatewayNoAndDeviceNo, propertyMap) ->
                    atomicInteger.addAndGet(propertyMap.values().size())
            );
            log.info("2、设备数(gatewayNo:deviceNo)：{}，点位数：{}", propertyMapMap.keySet().size(), atomicInteger.get());
        }
        localCacheConfig.setPropertyListMap(propertyListMap);
        {
            AtomicInteger atomicInteger = new AtomicInteger(0);
            propertyListMap.forEach((gatewayNoAndDeviceNo, propertyList) ->
                    atomicInteger.addAndGet(propertyList.size())
            );
            log.info("3、设备数(gatewayNo:deviceNo)：{}，点位数：{}", propertyListMap.keySet().size(), atomicInteger.get());
        }
        localCacheConfig.setPropertyMultimap(propertyMultimap);
        {
            AtomicInteger atomicInteger = new AtomicInteger(0);
            propertyMultimap.forEach((gatewayNo, deviceNoPropertyListMap) ->
                    deviceNoPropertyListMap.forEach((deviceNo, propertyList) ->
                            atomicInteger.addAndGet(propertyList.size())
                    )
            );
            log.info("4、设备数(gatewayNo)：{}，点位数：{}", propertyMultimap.keySet().size(), atomicInteger.get());
        }
    }

    @Override
    public void processMessage(String datetime, BrokerMessageDTO brokerMessageDTO) {
        String gatewayNo = brokerMessageDTO.getId();
        String deviceNo = brokerMessageDTO.getSn();
        Map<String, Object> currentData = brokerMessageDTO.getDt();
        Multimap<String, Map<String, List<IotDeviceProperty>>> propertyMultimap = localCacheConfig.getPropertyMultimap();
        // 检查外层键是否存在
        if (propertyMultimap.containsKey(gatewayNo)) {
            // 遍历所有这个键关联的映射
            for (Map<String, List<IotDeviceProperty>> deviceMap : propertyMultimap.get(gatewayNo)) {
                // 检查内层键是否存在
                if (deviceMap.containsKey(deviceNo)) {
                    List<IotDeviceProperty> devicePropertyList = deviceMap.get(deviceNo);
                    List<String> propertyNoList = currentData.keySet().stream().map(String::valueOf).collect(Collectors.toList());
                    List<IotDeviceProperty> currentPropertyList = devicePropertyList.stream().filter(e -> propertyNoList.contains(e.getPropertyNo())).collect(Collectors.toList());
                    Map<Long, List<IotDeviceProperty>> tableMap = currentPropertyList.stream().collect(Collectors.groupingBy(IotDeviceProperty::getDeviceId));
                    for (Map.Entry<Long, List<IotDeviceProperty>> entry : tableMap.entrySet()) {
                        Long deviceId = entry.getKey();
                        List<IotDeviceProperty> propertyList = entry.getValue();
                        Map<String, Object> srcMap = Maps.newLinkedHashMap();
                        Map<String, Object> insertMap = Maps.newLinkedHashMap();
                        for (IotDeviceProperty property : propertyList) {
                            String propertyNo = property.getPropertyNo();
                            String propertyType = property.getPropertyType();
                            String propertyConfig = property.getPropertyConfig();
                            Object valueObject = currentData.get(propertyNo);
                            if (null != valueObject) {
                                if (StringUtils.isNotBlank(String.valueOf(valueObject))) {
                                    srcMap.put(propertyNo, valueObject);
                                    insertMap.put(propertyNo, valueObject);
                                    // 公式处理：系数、偏移量、电表CT、电表PT等
                                    if (StringUtils.isNotBlank(propertyConfig) && StringUtils.isNotBlank(propertyType) && TDengineUtils.getNumberDateType().contains(propertyType.toUpperCase())) {
                                        IotExtConfig extConfig = JSONUtil.toBean(propertyConfig, IotExtConfig.class);
                                        String configType = extConfig.getType();
                                        BigDecimal ct = extConfig.getCt();
                                        BigDecimal pt = extConfig.getPt();
                                        BigDecimal factor = extConfig.getFactor();
                                        BigDecimal offset = extConfig.getOffset();
                                        // 先处理CT、PT，再处理系数及偏移量
                                        BigDecimal resultBigDecimal = Convert.toBigDecimal(valueObject)
                                                // 先处理电表点位的CT、PT
                                                .multiply(Optional.ofNullable(ct).orElse(BigDecimal.ONE))
                                                .multiply(Optional.ofNullable(pt).orElse(BigDecimal.ONE))
                                                // 再处理非电表点位的系数、偏移量
                                                .multiply(Optional.ofNullable(factor).orElse(BigDecimal.ONE))
                                                .add(Optional.ofNullable(offset).orElse(BigDecimal.ZERO));
                                        // 各种数据类型的转换及小数位处理
                                        String propertyTypeUpperCase = propertyType.toUpperCase();
                                        if (Arrays.asList(DateTypeEnum.TINYINT.getDateType(), DateTypeEnum.TINYINT_UNSIGNED.getDateType(),
                                                DateTypeEnum.SMALLINT.getDateType(), DateTypeEnum.SMALLINT_UNSIGNED.getDateType(),
                                                DateTypeEnum.INT.getDateType(), DateTypeEnum.INT_UNSIGNED.getDateType()).contains(propertyTypeUpperCase)) {
                                            insertMap.put(propertyNo, resultBigDecimal.setScale(0, RoundingMode.HALF_UP).intValue());
                                        } else if (Arrays.asList(DateTypeEnum.BIGINT.getDateType(), DateTypeEnum.BIGINT_UNSIGNED.getDateType()).contains(propertyTypeUpperCase)) {
                                            insertMap.put(propertyNo, resultBigDecimal.setScale(0, RoundingMode.HALF_UP).longValue());
                                        } else if (DateTypeEnum.FLOAT.getDateType().equalsIgnoreCase(propertyTypeUpperCase)) {
                                            insertMap.put(propertyNo, resultBigDecimal.setScale(6, RoundingMode.HALF_UP).floatValue());
                                        } else if (DateTypeEnum.DOUBLE.getDateType().equalsIgnoreCase(propertyTypeUpperCase)) {
                                            insertMap.put(propertyNo, resultBigDecimal.setScale(6, RoundingMode.HALF_UP).doubleValue());
                                        } else if (DateTypeEnum.DECIMAL.getDateType().equalsIgnoreCase(propertyTypeUpperCase)) {
                                            insertMap.put(propertyNo, resultBigDecimal.setScale(6, RoundingMode.HALF_UP));
                                        } else {
                                            // 其他类型，暂不处理
                                        }
                                    }
                                }
                            }
                        }
                        log.info("property_{}，datetime={}，gatewayNo={}，deviceNo={}，srcMap={}", deviceId, datetime, gatewayNo, deviceNo, srcMap);
                        log.info("property_{}，datetime={}，gatewayNo={}，deviceNo={}，insertMap={}", deviceId, datetime, gatewayNo, deviceNo, insertMap);
                        try {
                            iotDatasourceMapper.insertDataMap("property_" + deviceId, datetime, gatewayNo, deviceNo, insertMap);
                        } catch (Exception ex) {
                            log.error("插入数据失败：", ex);
                        }
                    }
                }
            }
        } else {
            log.error("当前数据报文对应的网关：{}不存在", gatewayNo);
        }
    }
}
